package extmodule;

import game.Action;
import game.model.Card;

import java.util.ArrayList;

import util.DebugOut;

/**
 * This class is a wrapper class to use the shared library MC.</br>
 * It converts specified interface for external proposals into the protocol the library expects. 
 * @author Witthold/Korol
 *
 */
public class ExtProposalMC implements IF_ExtModule_GetProposal {
	
	private ArrayList<ActionMC>	handHistory;
	private Card[][] 			holeCards;
	private ArrayList<Card>		boardCards;
	private int[] 				cashs;
	private int[] 				bets = {0, 0};
	private boolean 			dealer = false;
	
	
	private native boolean init();
	
	private native void setChips(int init_cash);
	
	private native void setCards(int c1, int c2);
	
	private native void showResult(boolean dealer, int card1, int card2); 
	
	private native int getNextAction(int player2_chips, boolean dealer);
	

	@Override
	public boolean initModule(int[] betSizes, int[] betStructure, int[] blindStructure, int ante, String playerNames, int[] cashOfPlayer) { 
			
		if( playerNames == null ) {	// TODO implement check if game type is limited 
			
			return false;
		
		} else {
	
			String[] names = playerNames.split(" ");
			
			if( names.length != 2 ) {
				
				return false;
			}

			handHistory = new ArrayList<ActionMC>();
			holeCards = new Card[names.length][2];
			boardCards = new ArrayList<Card>();

			cashs = new int[cashOfPlayer.length];
			cashs[0] = cashOfPlayer[0];
			cashs[1] = cashOfPlayer[1];
			
			if (!init() ) {
			
				return false;
			}
			
			setChips(cashs[0]);
			
			return true;
		}
	}
	
	@Override
	public void newHand(int button) {
		
		dealer = (button == 0) ? true : false;
		handHistory.clear();
		holeCards = new Card[holeCards.length][2];
		boardCards.clear();
		
		bets = new int[bets.length];
	}

	@Override
	public void newHoleCards(int player, int[] cardsAsInt) {
		
		Card[] cards = new Card[cardsAsInt.length];
		
		for(int i = 0; i < cardsAsInt.length; i++) {
			
			cards[i] = new Card(cardsAsInt[i]);
		}
		
		holeCards[player] = cards;
		
		if(player == 0) {
			
			setCards(convertCard(holeCards[0][0]),convertCard(holeCards[0][1]));
		}
	}

	@Override
	public void newAction(Action action) {
		
		char move = 0;
		int value = 0;
		
		if( action.isBlind() ) {
			move = 'b';
			value = action.getChange();
			
		} else if( action.isFold() ) {
			move = 'f';
			value = bets[action.getPlayer()];

		} else if( action.isCheck() ) {
			move = 'c';
			value = bets[action.getPlayer()];
			
		} else if( action.isCall() ) {
			move = 'C';
			value = bets[action.getPlayer()] + action.getChange();
			
		} else if( action.isRaise() ) {
			move = 'r';
			value = bets[action.getPlayer()] + action.getChange();
		}
		
		bets[action.getPlayer()] += value;
		cashs[action.getPlayer()] -= value;
		
		handHistory.add(new ActionMC(move, value)); 
		
		if( action.getPlayer() == 0 && action.getChange() != 0 ) { 
			
			setChips(cashs[0]);
		}
	}

	@Override
	public void newRound(int round, int[] newCards) {
		
		if(newCards != null) {

			for (int i = 0; i < newCards.length; i++) {
				
				boardCards.add(new Card(newCards[i]));
			}
		}
	}

	@Override
	public void showdown(int player, int gain) {
		
		cashs[player] += gain;
		
		if( player == 0 ) {
			
			setChips(cashs[0]);
			
		} else {
			
			int card1 = 0;
			int card2 = 0;
			
			if( holeCards[1][0] != null ) {

				card1 = convertCard(holeCards[1][0]);
				card2 = convertCard(holeCards[1][1]);
			
			}
			
			showResult(dealer, card1, card2);
		}
	}
	
	@Override
	public int getProposal() {
		
		int result = getNextAction(cashs[1], dealer);
		DebugOut.showVerboseJni("ExtProposalMC: getProposal: " + result);
		return result;
	}
	
	static {
		DebugOut.showVerboseJni("loading MC...");
		System.loadLibrary("MC");
	}

	
	/* ############################################
	 * These method are been called by the library.
	 * ############################################
	 */
	
	/**
	 * Returns the amount of actions in the gameStorage.
	 */
	public int getNumActions() {
		
		return handHistory.size();
	}
	
	/**
	 * Returns the type of the move with the given index.
	 * @param i	index of the move
	 * @return	type of move as char
	 */
	public char getOption(int i) {
		
		return handHistory.get(i).getMove();
	}
	
	/**
	 * Returns the value of the move with the given index.
	 * @param i	index of the move
	 * @return	value of the move
	 */
	public int getValue(int i) {
		
		return handHistory.get(i).getValue();
	}
	
	/**
	 * Returns the amount of board cards.
	 * @return	amount of board cards
	 */
	public int getNumBoardCards() {
		
		return boardCards.size();
	}
	
	/**
	 * Returns the board card revealed as the indexed card.
	 */
	public int getBoardCard(int i) {
		
		if (i < boardCards.size()) {

			int suit = boardCards.get(i).getSuitAsInt();
			int rank = boardCards.get(i).getRank();

			return convertCard(suit, rank);
			
		} else {
			
			return 0;
		}
	}

	/**
	 * Returns the given card converted into another format.
	 */
	private int convertCard(Card card) {
		
		return convertCard(card.getSuitAsInt(), card.getRank());
	}

	/**
	 * Returns the given card converted into another format.
	 */
	private int convertCard(int suit, int rank) {
				
		int result = 0;
		
		if( rank == 14 ) {	/* convert Aces */
			rank = 1;
		}
		
		switch (suit) {
		
		case 0:	/* diamonds */
			result = rank + 13;
			break;
		
		case 1:	/* hearts */
			result = rank + 26;
			break;
		
		case 2:	/* spades */
			result = rank + 39;
			break;
		
		case 3:	/* clubs */
			result = rank;
			break;

		default:
			break;
		}
		return result;
	}
}